package org.sqlproc.engine.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;

import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.util.EqualsHelper;
import org.hibernate.validator.constraints.NotEmpty;

/**
*
* Value object representing Engagement.
*/
@Entity
@Table(name = "ENGAGEMENT")
//@EntityListeners({org.hibernate.validator.event.JPAValidateListener.class
//})
public class Engagement {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id;
    @Column(name = "ROLE", nullable = false, length = 100)
    @NotEmpty
    private String role;
    @Column(name = "UUID", nullable = false, length = 255, unique = true)
    private String uuid;
    @ManyToOne(optional = false)
    @JoinColumn(name = "PERSON")
    @ForeignKey(name = "FK_ENGAGEMENT_PERSON")
    @NotEmpty
    private Person person;
    @ManyToOne(optional = false)
    @JoinColumn(name = "MEDIA")
    @ForeignKey(name = "FK_ENGAGEMENT_MEDIA")
    @OnDelete(action = OnDeleteAction.CASCADE)
    @NotEmpty
    private Media media;

    public Engagement() {
    }

    public Engagement(String role, Person person, Media media) {
        super();
//        Validate.notNull(role);
        this.role = role;
        this.person = person;
        this.media = media;
    }

    public Long getId() {
        return id;
    }

    /**
     * The id is not intended to be changed or assigned manually, but
     * for test purpose it is allowed to assign the id.
     */
    protected void setId(Long id) {
        if ((this.id != null) && !this.id.equals(id)) {
            throw new IllegalArgumentException(
                "Not allowed to change the id property.");
        }
        this.id = id;
    }

    public String getRole() {
        return role;
    }

    /**
     * Creates a copy of this instance, but with another role.
     */
    public Engagement withRole(String role) {
        if (EqualsHelper.equals(role, getRole())) {
            return this;
        }
        return new Engagement(role, getPerson(), getMedia());
    }

    /**
     * This domain object doesn't have a natural key
     * and this random generated identifier is the
     * unique identifier for this domain object.
     */
    public String getUuid() {

        // lazy init of UUID
        if (uuid == null) {
            uuid = java.util.UUID.randomUUID().toString();
        }
        return uuid;
    }

    @SuppressWarnings("unused")
    @PrePersist
    private void initUuid() {
        getUuid();
    }

    public Person getPerson() {
        return person;
    }

    /**
     * This reference can't be changed. Use constructor to assign value.
     * However, some tools need setter methods and sometimes the
     * referred object is not available at construction time. Therefore
     * this method is visible, but the actual reference can't be changed
     * once it is assigned.
     */
    public void setPerson(Person person) {

        // it must be possible to set null when deleting objects
        if ((person != null) && (this.person != null) &&
              !this.person.equals(person)) {
            throw new IllegalArgumentException(
                "Not allowed to change the person reference.");
        }
        this.person = person;
    }

    /**
     * Creates a copy of this instance, but with another person.
     */
    public Engagement withPerson(Person person) {
        if (EqualsHelper.equals(person, getPerson())) {
            return this;
        }
        return new Engagement(getRole(), person, getMedia());
    }

    public Media getMedia() {
        return media;
    }

    /**
     * This reference can't be changed. Use constructor to assign value.
     * However, some tools need setter methods and sometimes the
     * referred object is not available at construction time. Therefore
     * this method is visible, but the actual reference can't be changed
     * once it is assigned.
     */
    public void setMedia(Media media) {

        // it must be possible to set null when deleting objects
        if ((media != null) && (this.media != null) &&
              !this.media.equals(media)) {
            throw new IllegalArgumentException(
                "Not allowed to change the media reference.");
        }
        this.media = media;
    }

    /**
     * Creates a copy of this instance, but with another media.
     */
    public Engagement withMedia(Media media) {
        if (EqualsHelper.equals(media, getMedia())) {
            return this;
        }
        return new Engagement(getRole(), getPerson(), media);
    }

    /**
     * This method is used by equals and hashCode.
     * @return {{@link #getUuid}
     */
    public Object getKey() {
        return getUuid();
    }
}
